import random
from collections import deque
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QPainter, QBrush
from PyQt5.QtCore import QRect, Qt, QTimer, pyqtSignal, QUrl, pyqtSlot
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist
from myParam import MyParam, SnakeNode, GAME_STATUS, GAME_MOVE_DIRECTION, GAME_SPEED
from failDialog import FailDialog


# 根据得分设置游戏速度
def setGameTimerCount(curScore: int):
    if curScore < 5:
        return GAME_SPEED.GAME_SPEED_EASY
    elif 5 <= curScore < 7:
        return GAME_SPEED.GAME_SPEED_NORMAL
    elif 7 <= curScore < 10:
        return GAME_SPEED.GAME_SPEED_MEDIUM
    elif curScore >= 10:
        return GAME_SPEED.GAME_SPEED_HARD


# 判断是否得分
def judgeEatScoreNode(row, col, scoreNode: SnakeNode):
    return row == scoreNode.row and col == scoreNode.col


# 画snake
def printSnake(nodes, painter: QPainter):
    for node in nodes:
        painter.setBrush(QBrush(node.color))
        painter.drawRect(QRect(node.x1, node.y1, node.blockWidth, node.blockWidth))


# 游戏map窗口
class GameWindow(QWidget):
    # 自定义信号
    # 得分信号
    eatScoreNodeSig = pyqtSignal(int)
    # 得分变化信号
    scoreChangeSig = pyqtSignal(int)
    # 游戏失败信号
    gameFailSig = pyqtSignal()
    # 游戏状态修改信号
    gameStatusSig = pyqtSignal(int)

    def __init__(self, gameArgs: MyParam):
        super().__init__()
        self.score = 0
        self.gameArgs = gameArgs
        self.nodes = deque()
        self.scoreNode = self.genScoreNode()
        self.initMedia()
        self.genInitNode()
        self.initGameTimer()
        # 绑定自定义信号
        self.eatScoreNodeSig.connect(self.handleEatScoreNodeSig)
        self.gameFailSig.connect(self.handleGameFailSig)

        # 游戏失败弹窗
        self.msgDialog = FailDialog()
        self.msgDialog.failDialogBtnOkSig.connect(self.resetGame)

    # 定时器初始化
    def initGameTimer(self):
        self.game_timer = QTimer(self)
        self.game_timer.timeout.connect(self.gameTimerOut)
        self.game_timer.start(self.gameArgs.timerMs)

    # 定时器超时事件处理
    def gameTimerOut(self):
        # time = QDateTime.currentDateTime()
        # timedisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd")
        # print("timer ticker " + timedisplay)
        self.gameArgs.timerCount += 1
        if self.gameArgs.timerCount >= self.gameArgs.gameSpeed:
            self.gameArgs.timerCount = 0
            self.gameArgs.gameSpeed = setGameTimerCount(self.score)
            if self.gameArgs.gameStatus == GAME_STATUS.GAME_RUNNING:
                self.genMoveSnake()
                self.update()

    # 绘制事件
    def paintEvent(self, event):
        painter = QPainter(self)
        self.paintBorder(painter)
        self.printMap(painter)
        printSnake(self.nodes, painter)
        self.printScoreNode(self.scoreNode, painter)

    # 画边框
    def paintBorder(self, painter):
        painter.setPen(Qt.black)
        painter.drawRect(0, 0, self.width(), self.height())

    # 画方格map
    def printMap(self, painter):
        # 按行数、列数、方格大小画map
        for row in range(1, self.gameArgs.rows):
            for col in range(1, self.gameArgs.cols):
                painter.setBrush(QBrush(Qt.gray))
                x1 = self.gameArgs.blockWidth * row + (row - 1) * 4
                y1 = self.gameArgs.blockWidth * col + (col - 1) * 4
                painter.drawRect(QRect(x1, y1, self.gameArgs.blockWidth, self.gameArgs.blockWidth))

    # 生成初始snake节点
    def genInitNode(self):
        col = 15
        for row in range(10, 13):
            newNode = SnakeNode(row, col, self.gameArgs.blockWidth, Qt.red)
            self.nodes.append(newNode)

    # 移动snake
    def genMoveSnake(self):
        curDirection = self.gameArgs.moveDirection
        head = self.nodes[-1]
        row = head.row
        col = head.col

        # 更新前判断移动方向是否有效
        if (curDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT and
                self.gameArgs.keyDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_LEFT):
            self.gameArgs.moveDirection = self.gameArgs.keyDirection
        elif (curDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_LEFT and
              self.gameArgs.keyDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT):
            self.gameArgs.moveDirection = self.gameArgs.keyDirection
        elif (curDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_UP and
              self.gameArgs.keyDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_DOWN):
            self.gameArgs.moveDirection = self.gameArgs.keyDirection
        elif (curDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_DOWN and
              self.gameArgs.keyDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_UP):
            self.gameArgs.moveDirection = self.gameArgs.keyDirection
        else:
            self.gameArgs.moveDirection = curDirection

        if self.gameArgs.moveDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT:
            row += 1
        elif self.gameArgs.moveDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_LEFT:
            row -= 1
        elif self.gameArgs.moveDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_UP:
            col -= 1
        elif self.gameArgs.moveDirection == GAME_MOVE_DIRECTION.MOVE_DIRECTION_DOWN:
            col += 1

        # 判断是否失败
        if self.judgeGameFail(row, col):
            self.gameFailSig.emit()
            return

        # 判断是否得分
        if not judgeEatScoreNode(row, col, self.scoreNode):
            self.nodes.popleft()
        else:
            self.scoreNode = self.genScoreNode()
            self.eatScoreNodeSig.emit(1)
        self.nodes.append(SnakeNode(row, col, self.gameArgs.blockWidth, Qt.red))

    # 判断是否失败
    def judgeGameFail(self, row, col):
        gameFail = row == 0 or col == 0 or row == self.gameArgs.rows or col == self.gameArgs.cols
        for node in self.nodes:
            if node.row == row and node.col == col:
                gameFail = True
                break
        return gameFail

    # 处理游戏失败信号
    @pyqtSlot()
    def handleGameFailSig(self):
        self.gameArgs.gameStatus = GAME_STATUS.GAME_FINISH
        self.msgDialog.show()
        self.failPlayer.play()

    # 得分信号处理
    @pyqtSlot(int)
    def handleEatScoreNodeSig(self, addScore):
        print("eat score")
        self.score += addScore
        self.scoreChangeSig.emit(self.score)
        self.eatPlayer.play()

    # 重置游戏
    def resetGame(self):
        self.score = 0
        self.nodes = deque()
        self.scoreNode = self.genScoreNode()
        self.genInitNode()
        self.gameArgs.gameStatus = GAME_STATUS.GAME_PAUSE
        self.gameArgs.moveDirection = GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT
        self.update()
        self.gameStatusSig.emit(GAME_STATUS.GAME_RESET)

    # 生成得分节点
    def genScoreNode(self):
        row = random.randint(1, self.gameArgs.rows - 1)
        col = random.randint(1, self.gameArgs.cols - 1)
        return SnakeNode(row, col, self.gameArgs.blockWidth, Qt.yellow)

    # 画得分节点
    def printScoreNode(self, node, painter):
        painter.setBrush(QBrush(node.color))
        painter.drawRect(QRect(node.x1, node.y1, node.blockWidth, node.blockWidth))

    # 初始化音乐播放控件
    def initMedia(self):
        self.bgPlayer = QMediaPlayer()
        bgContent = QMediaContent(QUrl("wav/bg.mp3"))
        self.bgPlayerList = QMediaPlaylist()
        self.bgPlayerList.addMedia(bgContent)
        self.bgPlayerList.setPlaybackMode(QMediaPlaylist.Loop)
        self.bgPlayerList.setCurrentIndex(0)
        self.bgPlayer.setPlaylist(self.bgPlayerList)
        self.bgPlayer.play()

        self.eatPlayer = QMediaPlayer()
        eatContent = QMediaContent(QUrl("wav/eat.mp3"))
        self.eatPlayerList = QMediaPlaylist()
        self.eatPlayerList.addMedia(eatContent)
        self.eatPlayerList.setPlaybackMode(QMediaPlaylist.CurrentItemOnce)
        self.eatPlayerList.setCurrentIndex(0)
        self.eatPlayer.setPlaylist(self.eatPlayerList)

        self.failPlayer = QMediaPlayer()
        failContent = QMediaContent(QUrl("wav/fail.mp3"))
        self.failPlayerList = QMediaPlaylist()
        self.failPlayerList.addMedia(failContent)
        self.failPlayerList.setPlaybackMode(QMediaPlaylist.CurrentItemOnce)
        self.failPlayerList.setCurrentIndex(0)
        self.failPlayer.setPlaylist(self.failPlayerList)

    # 按键监听
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Left and self.gameArgs.moveDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT:
            print("key left pressed")
            self.gameArgs.keyDirection = GAME_MOVE_DIRECTION.MOVE_DIRECTION_LEFT
        elif event.key() == Qt.Key_Right and self.gameArgs.moveDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_LEFT:
            print("key right pressed")
            self.gameArgs.keyDirection = GAME_MOVE_DIRECTION.MOVE_DIRECTION_RIGHT
        elif event.key() == Qt.Key_Up and self.gameArgs.moveDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_DOWN:
            print("key up pressed")
            self.gameArgs.keyDirection = GAME_MOVE_DIRECTION.MOVE_DIRECTION_UP
        elif event.key() == Qt.Key_Down and self.gameArgs.moveDirection != GAME_MOVE_DIRECTION.MOVE_DIRECTION_UP:
            print("key down pressed")
            self.gameArgs.keyDirection = GAME_MOVE_DIRECTION.MOVE_DIRECTION_DOWN
        elif event.key() == Qt.Key_Space:
            print("key space pressed " + str(self.gameArgs.gameStatus))
            if self.gameArgs.gameStatus == GAME_STATUS.GAME_RUNNING:
                self.gameArgs.gameStatus = GAME_STATUS.GAME_PAUSE
            elif self.gameArgs.gameStatus == GAME_STATUS.GAME_PAUSE:
                self.gameArgs.gameStatus = GAME_STATUS.GAME_RUNNING
            self.gameStatusSig.emit(self.gameArgs.gameStatus)
        else:
            pass

    # 关闭窗口
    def close(self):
        self.killTimer(self.game_timer)
